package simtester.server

import static org.junit.Assert.*

import grails.test.mixin.*
import grails.test.mixin.support.*
import groovy.mock.interceptor.MockFor
import groovy.time.TimeCategory
import java.text.DateFormat
import java.text.SimpleDateFormat
import org.junit.*

/**
 * See the API for {@link grails.test.mixin.support.GrailsUnitTestMixin} for usage instructions
 */
@TestFor(TestRunController)
@Mock([User,TestCase,TestRun,ResultEntry,Iteration,CloudService,Category,TestRunService,Team,Type])
class TestRunControllerTests {
    static testusername = 'testuser'
    static testcasename = 'tc1'
    static testcasescript = 'some script'
    static testcase
    static DateFormat dateFormat = new SimpleDateFormat('MM/dd/yyyy HH:mm:ss.SSS')
    
    @Before
    void setUp() {
        def u = new User(username: testusername, password: 'pass')
        def team = new Team(name: 'svt')
        team.addToUsers(u)
        
        testcase = new TestCase(name: 'tc1', owner: u, script: 'some script')
        team.addToTestcases(testcase)
        team.save(failOnError: true)
    }

    @Test
    void createTestRun() {
        def mockCloudService = new MockFor(CloudService)
        def runTestAgentArgs
        mockCloudService.demand.runTestAgent { map ->
            runTestAgentArgs = map
        }        

        def id = 123L
        controller.cloudService = mockCloudService.proxyInstance()
        
        def testRepeatcount = '3'
        session.user = testusername
        request.method = 'POST'
        params.testcase = id.toString()
        params.repeat = 'count'
        params.repeatcount = testRepeatcount
        controller.create()
        
        assert runTestAgentArgs.testcaseId == id
        assert runTestAgentArgs.repeats == testRepeatcount.toInteger()
        assert response.redirectedUrl == '/testRun/running'
        mockCloudService.verify(controller.cloudService)
    }
    
    @Test
    void createTestRunWithDuration() {
        def mockCloudService = new MockFor(CloudService)
        def runTestAgentArgs
        mockCloudService.demand.runTestAgent { map ->
            runTestAgentArgs = map
        }

        def id = 123L
        controller.cloudService = mockCloudService.proxyInstance()
        
        def testHours = '3'
        def testMins = '5'
        def testSecs = '2'
        
        session.user = testusername
        request.method = 'POST'
        params.testcase = id.toString()
        params.repeat = 'duration'
        params.hours = testHours
        params.mins = testMins
        params.secs = testSecs
        controller.create()
        
        def expectedDuration
        use (TimeCategory) {
            expectedDuration = testHours.toInteger().hours + testMins.toInteger().minutes + testSecs.toInteger().seconds
        }

        assert runTestAgentArgs.testcaseId == id
        assert runTestAgentArgs.duration == expectedDuration.toMilliseconds()
        assert response.redirectedUrl == '/testRun/running'
        mockCloudService.verify(controller.cloudService)
    }
    
    @Test
    void createTestRunWithUsers() {
        def mockCloudService = new MockFor(CloudService)
        def runTestAgentArgs
        mockCloudService.demand.runTestAgent { map ->
            runTestAgentArgs = map
        }

        def id = 123L
        controller.cloudService = mockCloudService.proxyInstance()
        
        def testUsers = '2'
        def testRepeatcount = '3'
        session.user = testusername
        request.method = 'POST'
        params.testcase = id.toString()
        params.repeat = 'count'
        params.repeatcount = testRepeatcount
        params.users = testUsers
        controller.create()
        
        assert runTestAgentArgs.testcaseId == id
        assert runTestAgentArgs.repeats == testRepeatcount.toInteger()
        assert runTestAgentArgs.users   == testUsers.toInteger()
        assert response.redirectedUrl == '/testRun/running'
        mockCloudService.verify(controller.cloudService)
    }
    
    @Test
    void createTestRunForTestSuite() {
        def mockCloudService = new MockFor(CloudService)
        def runTestAgentArgs
        mockCloudService.demand.runTestAgent { map ->
            runTestAgentArgs = map
        }
        
        def id = 123L
        controller.cloudService = mockCloudService.proxyInstance()
        
        def userIds = ['12', '23', '34']
        
        def users = [:]
        users[userIds[0]] = 2
        users[userIds[1]] = 4
        users[userIds[2]] = 5
        
        def testRepeatcount = '3'
        session.user = testusername
        request.method = 'POST'
        params.testsuite = id.toString()
        params.repeat = 'count'
        params.repeatcount = testRepeatcount
        users.each { key, val ->
            params['tc' + key] = val
        }
        controller.create()

        mockCloudService.verify(controller.cloudService)
        assert response.redirectedUrl == '/testRun/running'
        assert runTestAgentArgs.testsuiteId == id
        assert runTestAgentArgs.repeats     == testRepeatcount.toInteger()
        assert runTestAgentArgs.users       == users
    }
    
    @Test
    void createLoadTestRunForTestSuite() {
        def mockCloudService = new MockFor(CloudService)
        def runTestAgentArgs
        mockCloudService.demand.runLoadTestAgent { map ->
            runTestAgentArgs = map
        }
        
        def id = 123L
        controller.cloudService = mockCloudService.proxyInstance()
        
        def tcIds = [45L, 62L]
        
        // testcase 1
        params['users-'  + tcIds[0]] = [1, 2, 3]
        params['permin-' + tcIds[0]] = [1, 2, 3]
        params['persec-' + tcIds[0]] = [4, 5, 6]
        params['hours-'  + tcIds[0]] = [2, 4, 6]
        params['mins-'   + tcIds[0]] = [3, 5, 7]
        params['secs-'   + tcIds[0]] = [4, 6, 8]
        
        // testcase 2
        params['users-'  + tcIds[1]] = [7, 8]
        params['permin-' + tcIds[1]] = [2, 3]
        params['persec-' + tcIds[1]] = [1, 2]
        params['hours-'  + tcIds[1]] = [1, 3]
        params['mins-'   + tcIds[1]] = [4, 6]
        params['secs-'   + tcIds[1]] = [5, 7]
        
        session.user = testusername
        request.method = 'POST'
        params.testsuite = id.toString()
        params.trname = 'testrun name'
        controller.create()
        
        mockCloudService.verify(controller.cloudService)
        assert response.redirectedUrl == '/testRun/running'
        assert runTestAgentArgs.testsuiteId == id
        assert runTestAgentArgs.testrunName == params.trname

        assert runTestAgentArgs.testcases.size() == 2
        runTestAgentArgs.testcases.eachWithIndex { tc, i ->
            assert tc.id == tcIds[i]
            
            int numPhases = params['users-' + tcIds[i]].size()
            assert numPhases == tc.phases.size()
            
            numPhases.times { p ->
                assert tc.phases[p].users == params['users-' + tcIds[i]][p]

                long perDuration = params['permin-' + tcIds[i]][p] * 60 +
                                   params['persec-' + tcIds[i]][p]
                perDuration *= 1000
                assert tc.phases[p].per == perDuration

                long duration = params['hours-' + tcIds[i]][p] * 3600 +
                                params['mins-'  + tcIds[i]][p] * 60 +
                                params['secs-'  + tcIds[i]][p]
                duration *= 1000
                assert tc.phases[p].duration == duration
            }
        }
    }

    @Test
    void listTestCasesForTestRunCreation() {
        session.user = testusername
        request.method = 'GET'
        def model = controller.create()
        
        assert model.testcases.size() == 1
        assert model.testcases[0].name   == testcasename
        assert model.testcases[0].script == testcasescript
        assert model.testcases[0].owner.username == testusername
    }
    
    @Test
    void listRunningTests() {
        def tr = new TestRun(testcase: testcase, status: 'running')
        assert tr.save()
        
        request.method = 'GET'
        session.user = testusername
        def model = controller.running()
        
        assert model.testruns.size() == 1
        assert model.testruns[0].status == 'running'
        assert model.testruns[0].testcase.name == testcasename
    }

    @Test
    void completeATestRun() {
        def mockCloudService = new MockFor(CloudService)
        mockCloudService.demand.scheduleRemovalOfInstances { testrun ->  }
        
        def tr = new TestRun(testcase: testcase, status: 'running')
        assert tr.save(flush:true)
        
        def id = tr.id
        request.method = 'PUT'
        request.json = '{status:"completed"}'
        params.id = id.toString()
        
        controller.cloudService = mockCloudService.proxyInstance()
        controller.update()
        mockCloudService.verify(controller.cloudService)
        
        assert response.json.msg == 'ok'
        def testrun = TestRun.get(id)
        assert testrun.status == 'completed'
        assert testrun.endDate != null
    }
    
    @Test
    void completeATestRunWithIdThatDoesNotExist() {
        def tr = new TestRun(testcase: testcase, status: 'running')
        assert tr.save()
        
        request.method = 'PUT'
        request.json = '{status:"completed"}'
        def wrongId = 71L
        params.id = wrongId
        
        controller.update()
        
        assert response.json.msg   == 'failed'
        assert response.json.error == "cannot find ID $wrongId"
    }
    
    @Test
    void acceptTestResults() {
        def saveArgs
        def mockTestRunService = new MockFor(TestRunService)
        mockTestRunService.demand.saveTestRunResults { arg1, arg2 ->
            saveArgs = [arg1, arg2]
        }
        
        def results = "Some results"
        
        def id = 4L
        request.method = 'POST'
        request.json = "{\"results\":\"" + results + "\"}"
        params.id = id.toString()
        
        controller.testRunService = mockTestRunService.proxyInstance()
        controller.sendresults()
        
        mockTestRunService.verify(controller.testRunService)
        assert response.json.msg == 'ok'
        assert saveArgs[0] == id
        assert saveArgs[1] == results
    }

    @Test
    void viewResultsOfATestRun() {
        def tr = new TestRun(testcase: testcase, status: 'completed')
        assert tr.save()

        def results = [
            new ResultEntry(timestamp: dateFormat.parse('01/21/2012 15:11:23.159'), entry: 'Title: Google'),
            new ResultEntry(timestamp: dateFormat.parse('01/21/2012 15:11:23.164'), entry: 'Test Completed'),
            new ResultEntry(timestamp: dateFormat.parse('01/21/2012 15:11:23.170'), entry: 'Sending request to complete test execution')
        ]
        
        results.each { r ->
            r.level = 'INFO'
            r.iteration = 1
            r.user = 1
            r.testrun = tr
            r.save()
        }
        
        request.method = 'GET'
        params.id      = tr.id.toString()
        
        def testrun = controller.results().testrun
        assert testrun.status == 'completed'
        
        def found = testrun.findResults()
        assert found.size() == 3
        found.eachWithIndex { x, i ->
            assert x.time == results[i].timestamp.time
        }
        
        assert found[0].output == '01/21/2012 15:11:23.159 INFO: Title: Google'
        assert found[1].output == '01/21/2012 15:11:23.164 INFO: Test Completed'
        assert found[2].output == '01/21/2012 15:11:23.170 INFO: Sending request to complete test execution'
    }

}

